#include "lio3d.h"

namespace Lio {

DEFSRC(glBegin,glBeginIndex);
DEFSRC(glNewList,glNewListIndex);

CSTR GlErrorToStr(GLenum error)
{
	switch(error)
	{
		case GL_NO_ERROR:			return _T("No error has been recorded. The value of this symbolic constant is guaranteed to be zero.");
		case GL_INVALID_ENUM:		return _T("An unacceptable value is specified for an enumerated argument. The offending function is ignored, having no side effect other than to set the error flag.");
		case GL_INVALID_VALUE:		return _T("A numeric argument is out of range. The offending function is ignored, having no side effect other than to set the error flag.");
		case GL_INVALID_OPERATION:	return _T("The specified operation is not allowed in the current state. The offending function is ignored, having no side effect other than to set the error flag.");
		case GL_STACK_OVERFLOW:		return _T("This function would cause a stack overflow. The offending function is ignored, having no side effect other than to set the error flag.");
		case GL_STACK_UNDERFLOW:	return _T("This function would cause a stack underflow. The offending function is ignored, having no side effect other than to set the error flag.");
		case GL_OUT_OF_MEMORY:		return _T("There is not enough memory left to execute the function. The state of OpenGL is undefined, except for the state of the error flags, after this error is recorded.");
		default:					return _T("Unknown error.");
	}
}

#define NORM_FACTOR(N) FastInvSqrt(float(N[0]*N[0]+N[1]*N[1]+N[2]*N[2]))
GLvoid drawEllipsoid(float a, float b, float c, int nLat, int nLon)
{
	if(a<0)
		a=1.0f;
	if(b<0)
		b=a;
	if(c<0)
		c=a;
	if(nLat<=0)
		nLat=2;
	if(nLon<=0)
		nLon=2*nLat;
	float tmp;
	double 
		u=0,v=0,du,dv,
		P[3],N[3],
		lat=-0.5*PI,lon=0,
		dlat,dlon,
		clat,slat,
		clat0=0,slat0=-1,
		clon,slon,
		clon0=1,slon0=0;

	dlat=PI/nLat;
	dlon=2*PI/nLon;
	du=INV(nLon);
	dv=INV(nLat);

	glBegin(GL_QUADS); // Start Drawing Quads
	for(int i=0;i<nLat;i++)
	{
		// lat update
		lat+=dlat;
		clat=cos(lat);
		slat=sin(lat);
		
		u=0;
		for(int j=0;j<nLon;j++)
		{
			// lon update
			lon+=dlon;
			clon=cos(lon);
			slon=sin(lon);
			
			///////////////////////////////////////////
			// 
			//		(u,v+dv)	(u+du,v+dv)
			//		P3----------P2	lon
			//		|			|
			//		|			|	/\
			//		|			|
			//		P0----------P1	lon0
			//		(u,v)		(u+du,v)	
			//		lat0	>	lat
			//
			///////////////////////////////////////////

			// P0: lat = lat0, lon = lon0
			P[0]= a*clon0*clat0;
			P[1]= b*slon0*clat0;
			P[2]= c*slat0;
			N[0]= b*c*clon0*clat0;
			N[1]= a*c*slon0*clat0;
			N[2]= a*b*slat0;
			tmp=NORM_FACTOR(N);
			glNormal3d(N[0]*tmp, N[1]*tmp, N[2]*tmp);
			glTexCoord2d(u,	v);
			glVertex3dv(P);
			
			// P1: lat = lat0, lon = lon
			P[0]= a*clon*clat0;
			P[1]= b*slon*clat0;
			P[2]= c*slat0;
			N[0]= b*c*clon*clat0;
			N[1]= a*c*slon*clat0;
			N[2]= a*b*slat0;
			tmp=NORM_FACTOR(N);
			glNormal3d(N[0]*tmp, N[1]*tmp, N[2]*tmp);
			glTexCoord2d(u+du,	v);
			glVertex3dv(P);

			// P2: lat = lat, lon = lon
			P[0]= a*clon*clat;
			P[1]= b*slon*clat;
			P[2]= c*slat;
			N[0]= b*c*clon*clat;
			N[1]= a*c*slon*clat;
			N[2]= a*b*slat;
			tmp=NORM_FACTOR(N);
			glNormal3d(N[0]*tmp, N[1]*tmp, N[2]*tmp);
			glTexCoord2d(u+du,	v+dv);
			glVertex3dv(P);

			// P3: lat = lat, lon = lon0
			P[0]= a*clon0*clat;
			P[1]= b*slon0*clat;
			P[2]= c*slat;
			N[0]= b*c*clon0*clat;
			N[1]= a*c*slon0*clat;
			N[2]= a*b*slat;
			tmp=NORM_FACTOR(N);
			glNormal3d(N[0]*tmp, N[1]*tmp, N[2]*tmp);
			glTexCoord2d(u,	v+dv);
			glVertex3dv(P);
			
			clon0=clon;
			slon0=slon;
			u+=du;
		}
		clat0=clat;
		slat0=slat;
		v+=dv;
	}
	glEnd();
}

GLvoid drawSphere(float R, int nLat, int nLon)
{
	if(R<0)
		R=1.0f;
	if(nLat<=0)
		nLat=2;
	if(nLon<=0)
		nLon=2*nLat;
	double
		u=0,v=0,du,dv,
		N[3],
		lat=-0.5*PI,lon=0,
		dlat,dlon,
		clat,slat,
		clat0=0,slat0=-1,
		clon,slon,
		clon0=1,slon0=0;

	dlat=PI/nLat;
	dlon=2*PI/nLon;
	du=INV(nLon);
	dv=INV(nLat);

	glBegin(GL_QUADS); // Start Drawing Quads
	for(int i=0;i<nLat;i++)
	{
		// lat update
		lat+=dlat;
		clat=cos(lat);
		slat=sin(lat);
		
		u=0;
		for(int j=0;j<nLon;j++)
		{
			// lon update
			lon+=dlon;
			clon=cos(lon);
			slon=sin(lon);
			
			///////////////////////////////////////////
			// 
			//		(u,v+dv)	(u+du,v+dv)
			//		P3----------P2	lon
			//		|			|
			//		|			|	/\
			//		|			|
			//		P0----------P1	lon0
			//		(u,v)		(u+du,v)	
			//		lat0	>	lat
			//
			///////////////////////////////////////////

			// P0: lat = lat0, lon = lon0
			N[0]= clon0*clat0;
			N[1]= slon0*clat0;
			N[2]= slat0;
			glNormal3d(N[0], N[1], N[2]);
			glTexCoord2d(u,	v);
			glVertex3d(N[0]*R, N[1]*R, N[2]*R);
			
			// P1: lat = lat0, lon = lon
			N[0]= clon*clat0;
			N[1]= slon*clat0;
			N[2]= slat0;
			glNormal3d(N[0], N[1], N[2]);
			glTexCoord2d(u+du,	v);
			glVertex3d(N[0]*R, N[1]*R, N[2]*R);

			// P2: lat = lat, lon = lon
			N[0]= clon*clat;
			N[1]= slon*clat;
			N[2]= slat;
			glNormal3d(N[0], N[1], N[2]);
			glTexCoord2d(u+du,	v+dv);
			glVertex3d(N[0]*R, N[1]*R, N[2]*R);

			// P3: lat = lat, lon = lon0
			N[0]= clon0*clat;
			N[1]= slon0*clat;
			N[2]= slat;
			glNormal3d(N[0], N[1], N[2]);
			glTexCoord2d(u,	v+dv);
			glVertex3d(N[0]*R, N[1]*R, N[2]*R);
			
			clon0=clon;
			slon0=slon;
			u+=du;
		}
		clat0=clat;
		slat0=slat;
		v+=dv;
	}
	glEnd();
}
GLvoid drawEllipse(float x,float y,float a,float b, int n, BOOL fill)
{
	if(a<0)
		a=1.0f;
	if(b<0)
		b=a;
	if(n<4)
		n=4;
	float
		angle=0,step;

	step=float(2*PI/n);
	
	glPushAttrib(GL_ENABLE_BIT);
	glDisable(GL_LIGHTING);
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glTranslatef(x,y,0);

	if(fill)
	{
		glBegin(GL_POLYGON);
	}
	else
	{
		glBegin(GL_LINE_STRIP); // Start Drawing Quads
	}
	glVertex2f(a,0);
	for(int i=0;i<n;i++)
	{
		angle+=step;
		glVertex2f(cos(angle)*a,sin(angle)*b);
	}
	glEnd();
	
	glPopAttrib();
	glPopMatrix();
}
GLfloat getLineWidthProps(GLfloat *min, GLfloat *max)
{
	GLfloat values[2]={0};
	glGetFloatv (GL_LINE_WIDTH_RANGE, values);
	if(min)
		*min=values[0];
	if(max)
		*max=values[1];
	glGetFloatv (GL_LINE_WIDTH_GRANULARITY, values);
	return values[0];
}
GLvoid drawParallel(	// Draw A Parallelipiped
				float a, float b, float c,
				float u, float v,
				float n) 
{
	glBegin(GL_QUADS); // Start Drawing Quads
		// Front Face
		glNormal3f( 0, 0, n); // Normal Facing Forward
		// Bottom Left Of The Texture and Quad
		glTexCoord2f(0, 0); glVertex3f(-a, -b, c);
		// Bottom Right Of The Texture and Quad
		glTexCoord2f(u, 0); glVertex3f( a, -b, c);
		// Top Right Of The Texture and Quad
		glTexCoord2f(u, v); glVertex3f( a, b, c);
		// Top Left Of The Texture and Quad
		glTexCoord2f(0, u); glVertex3f(-a, b, c);

		// Back Face
		glNormal3f( 0, 0,-n); // Normal Facing Away
		// Bottom Right Of The Texture and Quad
		glTexCoord2f(u, 0); glVertex3f(-a, -b, -c);
		// Top Right Of The Texture and Quad
		glTexCoord2f(u, v); glVertex3f(-a, b, -c);
		// Top Left Of The Texture and Quad
		glTexCoord2f(0, u); glVertex3f( a, b, -c);
		// Bottom Left Of The Texture and Quad
		glTexCoord2f(0, 0); glVertex3f( a, -b, -c);

		// Top Face
		glNormal3f( 0, n, 0); // Normal Facing Up
		// Top Left Of The Texture and Quad
		glTexCoord2f(0, v); glVertex3f(-a, b, -c);
		// Bottom Left Of The Texture and Quad
		glTexCoord2f(0, 0); glVertex3f(-a, b, c);
		// Bottom Right Of The Texture and Quad
		glTexCoord2f(u, 0); glVertex3f( a, b, c);
		// Top Right Of The Texture and Quad
		glTexCoord2f(u, v); glVertex3f( a, b, -c);

		// Bottom Face
		glNormal3f( 0,-n, 0); // Normal Facing Down
		// Top Right Of The Texture and Quad
		glTexCoord2f(u, v); glVertex3f(-a, -b, -c);
		// Top Left Of The Texture and Quad
		glTexCoord2f(0, v); glVertex3f( a, -b, -c);
		// Bottom Left Of The Texture and Quad
		glTexCoord2f(0, 0); glVertex3f( a, -b, c);
		// Bottom Right Of The Texture and Quad
		glTexCoord2f(u, 0); glVertex3f(-a, -b, c);

		// Right face
		glNormal3f( n, 0, 0); // Normal Facing Right
		// Bottom Right Of The Texture and Quad
		glTexCoord2f(u, 0); glVertex3f( a, -b, -c);
		// Top Right Of The Texture and Quad
		glTexCoord2f(u, v); glVertex3f( a, b, -c);
		// Top Left Of The Texture and Quad
		glTexCoord2f(0, v); glVertex3f( a, b, c);
		// Bottom Left Of The Texture and Quad
		glTexCoord2f(0, 0); glVertex3f( a, -b, c);

		// Left Face
		glNormal3f(-n, 0, 0); // Normal Facing Left
		// Bottom Left Of The Texture and Quad
		glTexCoord2f(0, 0); glVertex3f(-a, -b, -c);
		// Bottom Right Of The Texture and Quad
		glTexCoord2f(u, 0); glVertex3f(-a, -b, c);
		// Top Right Of The Texture and Quad
		glTexCoord2f(u, v); glVertex3f(-a, b, c);
		// Top Left Of The Texture and Quad
		glTexCoord2f(0, v); glVertex3f(-a, b, -c);
	glEnd(); // Done Drawing Quads
}
BOOL Texture::create(Image * image)
{
	error=glGetError();
	if(!image||!image->getSize())
		return FALSE;
	format=getPixelFormat(image);
	width=image->width;
	height=image->height;
	data=image->data;
	glGenTextures(1, &id);
	glBindTexture(GL_TEXTURE_2D, id);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,wrap);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,wrap);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,minFilter);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,magFilter);
	if(mipmapped)
	{
		gluBuild2DMipmaps(
			GL_TEXTURE_2D,	// 2D image
			childs,			// number of color childs
			width,			// width
			height,			// height
			format,			// color model
			dataFormat,		// data word type
			data);			// data
	}
	else
	{
		glTexImage2D(
			GL_TEXTURE_2D,	// 2D image
			levelDetail,	// detail level
			childs,			// number of color childs
			width,			// width
			height,			// height
			border,			// border
			format,			// color model
			dataFormat,		// data word type
			data);			// data
	}
	error=glGetError();
	return error?FALSE:TRUE;
}
void Texture::destroy()
{
	width=0;
	height=0;
	id=0;
	data=NULL;
	glDeleteTextures(1, &id);
}
Font::Font():
		height(0),width(0),
		first(0),count(0),
		x(0),y(0),z(0),zoom(0),align(ALIGN_LEFT),
		color(0),expand(0),
		type(FONT_BITMAP),
		charset(ANSI_CHARSET),quality(ANTIALIASED_QUALITY),
		bold(FALSE),italic(FALSE),underline(FALSE),strike(FALSE),
		escapement(0),orientation(0),
		charWidths(NULL),loadMetrix(TRUE),glyphMetrix(NULL),
		polygones(TRUE),deviation(0),depth(0),
		colls(0),rows(0),shift(0),
		textureID(0),textureSource(NULL)
{
	setFace(NULL);
	ClearStruct(textMetrix);
}
Font::~Font()
{
	destroy();
}
void Font::setFace(CSTR newFace)
{
	if(newFace)
	{
		_tcsncpy(face,newFace,FONT_FACE_SIZE);
		face[FONT_FACE_SIZE-1]=0;
	}
	else
	{
		ClearMemory(face,FONT_FACE_SIZE);
	}
}
BOOL Font::create(HDC hDC) 
{
	destroy();
	if(first<0)
		first=0;
	if(count<=0)
	{
		count=rows*colls;
		if(count<=0)
			count=256;
	}
	list = glGenLists(count); // Storage For count Characters
	if(type==FONT_TEXTURE)
	{
		if(colls<=0)
		{
			if(rows>0)
				colls=count/rows;
			else
				colls=__int(sqrtf((float)count));
		}
		else if(colls>count)
		{
			colls=count;
		}
		rows=count/colls;
		
		if(!height)
			height=16;
		else if(height<0)
			height=-height;
		if(!width)
			width=height;
		if(!shift)
			shift=(float)width;
		if(textureSource&&texture.create(textureSource))
			textureID=texture.getID();

		float cx; // Holds Our X Character Coord
		float cy; // Holds Our Y Character Coord
		float cnw=1.0f/colls; // Normalized Cell Width
		float cnh=1.0f/rows; // Normalized Cell Height
		int w=width;
		int h=height;
		for (int c=0; c<count; c++) // Loop Through All 256 Lists
		{
			cx=float(c%colls)/colls; // Normalized 0 Position Of Current Character
			cy=float(c/colls)/rows; // Normalized 1 Position Of Current Character
			glNewList(list+c,GL_COMPILE); // Start Building A List
				glBegin(GL_QUADS); // Use A Quad For Each Character
					glTexCoord2f(cx,1-cy-cnh); // Texture Coord (Bottom Left)
					glVertex2i(0,0); // Vertex Coord (Bottom Left)
					glTexCoord2f(cx+cnw,1-cy-cnh); // Texture Coord (Bottom Right)
					glVertex2i(w,0); // Vertex Coord (Bottom Right)
					glTexCoord2f(cx+cnw,1-cy); // Texture Coord (Top Right)
					glVertex2i(w,h); // Vertex Coord (Top Right)
					glTexCoord2f(cx,1-cy); // Texture Coord (Top Left)
					glVertex2i(0,h); // Vertex Coord (Top Left)
				glEnd(); // Done Building Our Quad (Character)
				glTranslatef(shift,0,0); // Move To The Right Of The Character
			glEndList(); // Done Building The Display List
		} // Loop Until All count Characters Are Built
	}
	else
	{
		if(!hDC)
		{
			destroy();
			return FALSE;
		}
		HFONT fontID; // Windows Font ID
		HFONT oldFontID; // Used For Good House Keeping
		int weight=0;
		if(bold)
			weight=FW_BOLD;
		fontID = CreateFont(height, // Height Of Font, Based On Character (-) Or Cell (+)
							width, // Width Of Font (0 = use default value)
							escapement, // Angle Of Escapement
							orientation, // Orientation Angle
							weight, // Font Weight
							italic, // Italic
							underline, // Underline
							strike, // Strikeout
							charset, // Character Set Identifier
							OUT_TT_PRECIS, // TrueType Output Precision
							CLIP_DEFAULT_PRECIS, // Clipping Precision
							quality, // Output Quality
							FF_DONTCARE|DEFAULT_PITCH, // Pitch And Family
							face); // Font Face Name
		if(!fontID)
		{
			destroy();
			return FALSE;
		}
		oldFontID = (HFONT)SelectObject(hDC, fontID); // Selects The Font We Want
		// Builds count Characters Starting At Character first
		GetTextMetrics(hDC,&(textMetrix));
		if(type==FONT_OUTLINE)
		{
			if(loadMetrix)
				NEW_ARRAY(glyphMetrix,GLYPHMETRICSFLOAT,count);
			else
				glyphMetrix=NULL;
			if(!wglUseFontOutlines(	hDC, 
									first,
									count,
									list,
									deviation, // Specifies the maximum chordal deviation from the original outlines
									depth, // Specifies how much a font is extruded in the negative z direction
									polygones, // Specifies the format, either WGL_FONT_LINES (0) or WGL_FONT_POLYGONS (1), to use in the display lists
									glyphMetrix)) // Points to an array of count GLYPHMETRICSFLOAT structures that is to receive the metrics of the glyphs
			{
				SelectObject(hDC, oldFontID); // Restore The Old Font
				DeleteObject(fontID); // Delete The Font
				destroy();
				return FALSE;
			}
		}
		else
		{
			if(loadMetrix)
				NEW_ARRAY(charWidths,int,count);
			else
				charWidths=NULL;
			if(!wglUseFontBitmaps(	hDC,
									first,
									count,
									list))
			{
				SelectObject(hDC, oldFontID); // Restore The Old Font
				DeleteObject(fontID); // Delete The Font
				destroy();
				return FALSE;
			}
			if(charWidths)
			{
				if(!GetCharWidth32(	hDC,
									first,
									first+count-1,
									charWidths))
				{
					if(!GetCharWidth(	hDC,
										first,
										first+count-1,
										charWidths))
					{
						DELETE_ARRAY(charWidths);
						charWidths=NULL;
					}
				}
			}
		}
		SelectObject(hDC, oldFontID); // Restore The Old Font
		DeleteObject(fontID); // Delete The Font
	}
	return TRUE;
}

void Font::destroy() // Delete The Font List
{
	if(!list)
		return;
	glDeleteLists(list, count); // Delete All Characters
	list=0;
	if(glyphMetrix)
	{
		DELETE_ARRAY(glyphMetrix);
		glyphMetrix=NULL;
	}
	if(charWidths)
	{
		DELETE_ARRAY(charWidths);
		charWidths=NULL;
	}
}
int Font::getTextIndex(CSTR text, float width, int index, int length)
{
	if(!loaded()||width<0)
		return -1;
	int maxLength = strLen(text);
	if(index<0||index>=maxLength)
		index=0;
	if(length<0||length>maxLength-index)
		length=maxLength-index;
	if(length<=0)
		return -1;
	int code;
	text+=index;
	switch(type)
	{
	case FONT_OUTLINE:
		if(glyphMetrix)
		{
			for(int i=0;i<length;i++)
			{
				code=text[i]-first;
				if(code<0||code>=count)
					return 0;
				width-=glyphMetrix[code].gmfCellIncX;
				if(width<0)
					return index+i-1;
			}
		}
		break;
	case FONT_BITMAP:
		if(charWidths)
		{
			for(int i=0;i<length;i++)
			{
				code=text[i]-first;
				if(code<0||code>=count)
					return 0;
				width-=float(charWidths[code]);
				if(width<0)
					return index+i-1;
			}
		}
		break;
	case FONT_TEXTURE:
		if(shift)
		{
			int count=__int(width/shift);
			if(count<length)
				return index+count;
		}
		break;
	default:
		return -1;
	}
	return index+length;
}
float Font::getTextWidth(CSTR text, int index, int length)
{
	if(!text||!loaded())
		return FALSE;
	if(index<0)
		index=0;
	if(length<0)
		length=strLen(text)-index;
	if(length<=0)
		return FALSE;

	int code;
	float width=0;
	text+=index;
	switch(type)
	{
	case FONT_OUTLINE:
		if(glyphMetrix)
		{
			for(int i=0;i<length;i++) // Loop To Find Text Length
			{
				code=text[i]-first;
				if(code<0||code>=count)
					return 0;
				width+=glyphMetrix[code].gmfCellIncX;
			}
		}
		break;
	case FONT_BITMAP:
		if(charWidths)
		{
			for(int i=0;i<length;i++) // Loop To Find Text Length
			{
				code=text[i]-first;
				if(code<0||code>=count)
					return 0;
				width+=float(charWidths[code]);
			}
		}
		break;
	case FONT_TEXTURE:
		if(shift)
		{
			width=length*shift;
		}
		break;
	default:
		return 0;
	}
	return width;
}
BOOL Font::print(CSTR text, int index, int length)
{
	if(!text||!loaded())
		return FALSE;
	if(index<0)
		index=0;
	if(length<0)
		length=strLen(text)-index;
	if(length<=0)
		return FALSE;

	glPushAttrib(GL_ENABLE_BIT); // Push The Enable Bits
	glPushAttrib(GL_CURRENT_BIT); // Push The Current Bits
	glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
	glPushMatrix(); // Store The Modelview Matrix
	if(color)
		setColor(color);
	text+=index;
	float alignedX=x;
	switch(align)
	{
	case ALIGN_CENTER:
		alignedX-=0.5f*getTextWidth(text,0,length);
		break;
	case ALIGN_RIGHT:
		alignedX-=getTextWidth(text,0,length);
		break;
	}
	switch(type)
	{
		case FONT_BITMAP:
			glDisable(GL_TEXTURE_2D); // Enable Texture Mapping
			glDisable(GL_LIGHTING);
			glRasterPos3d(alignedX,y,z);
			break;
		case FONT_TEXTURE:
			glEnable(GL_TEXTURE_2D); // Enable Texture Mapping
			//glEnable(GL_BLEND); // Enable Blending
			//glDisable(GL_LIGHTING);
			glBindTexture(GL_TEXTURE_2D, textureID);			// Select Our Font Texture
			//glDisable(GL_DEPTH_TEST);							// Disables Depth Testing
		case FONT_OUTLINE:
			glTranslatef(alignedX,y,z);
			if(zoom>0)
				glScaled(zoom,zoom,zoom);
			break;
	}
	
	glPushAttrib(GL_LIST_BIT); // Pushes The Display List Bits To Keep Other Lists Inaffected
	glListBase(list - first+expand*128); // Sets The Base Character to first
	// Draws The Display List Text
	glCallLists(length, GL_CHAR_SIZE, text);
	glPopAttrib(); // Pops The Display List Bits
	
	glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
	glPopMatrix(); // Restore The Old Modelview Matrix
	
	glPopAttrib(); // Pops The Current Bits
	glPopAttrib(); // Pops The Enable Bits
	return TRUE;
}
void glPrint(Font *font, CSTR fmt, ...)
{
	if (font&&font->loaded()&&fmt) // If There's No Text
	{
		_TCHAR text[MAX_PATH]; // Holds Our String
		va_list ap; // Pointer To List Of Arguments
		//ClearMemory(text,sizeof(text));
		va_start(ap, fmt); // Parses The String For Variables
			_vsntprintf(text, sizeof(text)-1, fmt, ap); // And Converts Symbols To Actual Numbers
		va_end(ap); // Results Are Stored In Text
		font->print(text);
	}
}
void rotObject(CMATRIX3 R, int count, float vertices[][3][3], float out[][3][3])
{
	if(out==NULL)
	{
		out=vertices;
	}
	for(int i=0;i<count;i++)
	{
		mulVector(R,vertices[i][0],out[i][0]);
		mulVector(R,vertices[i][1],out[i][1]);
		mulVector(R,vertices[i][2],out[i][2]);
	}	
}
void rotObject(CQUAT q, int count, float vertices[][3][3], float out[][3][3])
{
	if(out==NULL)
		out=vertices;
	for(int i=0;i<count;i++)
	{
		rotVector(q,vertices[i][0],out[i][0]);
		rotVector(q,vertices[i][1],out[i][1]);
		rotVector(q,vertices[i][2],out[i][2]);
	}	
}
void transObject(float T[3], int count, float vertices[][3][3], float out[][3][3])
{
	if(out==NULL)
		out=vertices;
	for(int i=0;i<count;i++)
	{
		addVector(T,vertices[i][0],out[i][0]);
		addVector(T,vertices[i][1],out[i][1]);
		addVector(T,vertices[i][2],out[i][2]);
	}	
}

BOOL setMemAlign(int memWidth, BOOL pack)
{
	if(memWidth<=0)
		return FALSE;
	if(memWidth%8==0)
		glPixelStorei(pack?GL_PACK_ALIGNMENT:GL_UNPACK_ALIGNMENT,8);
	else if(memWidth%4==0)
		glPixelStorei(pack?GL_PACK_ALIGNMENT:GL_UNPACK_ALIGNMENT,4);
	else if(memWidth%2==0)
		glPixelStorei(pack?GL_PACK_ALIGNMENT:GL_UNPACK_ALIGNMENT,2);
	else
		glPixelStorei(pack?GL_PACK_ALIGNMENT:GL_UNPACK_ALIGNMENT,1);
	return TRUE;
}
int getMemAlign(BOOL pack)
{
	int align=0;
	glGetIntegerv(pack?GL_PACK_ALIGNMENT:GL_UNPACK_ALIGNMENT,&align);
	return align;
}
GLuint getPixelFormat(const Image * image)
{
	switch(image->comps)
	{
	case PIXEL_COMP_RGBA:
		return GL_RGBA;
	case PIXEL_COMP_RGB:
		return GL_RGB;
	default:
		return GL_LUMINANCE;
	}
}
void drawImage(const Image * image, float x, float y, int align)
{
	if(!image||!image->data)return;
	if(align<0)
		setMemAlign(image->width, FALSE);
	else if(align>0)
		setMemAlign(align, FALSE);
	glRasterPos2d(x,y);
	glDrawPixels(image->width,image->height,getPixelFormat(image),GL_UNSIGNED_BYTE,image->data);
}
void readImage(Image * image, float x, float y, int align)
{
	if(align<0)
		setMemAlign(image->width, TRUE);
	else if(align>0)
		setMemAlign(align, TRUE);
	glReadPixels((UINT)x,(UINT)y,image->width,image->height,getPixelFormat(image),GL_UNSIGNED_BYTE,image->data);
}
void drawLine(
		float startX, float startY, float startZ,
		float endX, float endY, float endZ,
		DWORD color, float line)
{
	if(line)
		glLineWidth(line);
	if(color)
		setColor(color);
	glBegin(GL_LINES);
		glVertex3f( startX, startY, startZ);
		glVertex3f( endX, endY, endZ);
	glEnd();
}
void drawLine2(
		float startX, float startY,
		float endX, float endY,
		DWORD color, float line)
{
	if(line)
		glLineWidth(line);
	if(color)
		setColor(color);
	glBegin(GL_LINES);
		glVertex2f( startX, startY);
		glVertex2f( endX, endY);
	glEnd();
}
void drawBox(
		float left, float top,
		float width, float height,
		DWORD color,
		float line)
{
	if(color)
		setColor(color);
	if(line)
		glLineWidth(line);
	glBegin(GL_LINE_STRIP);
		glVertex2f( left, top);
		glVertex2f( left, top+height);
		glVertex2f( left+width, top+height);
		glVertex2f( left+width, top);
		glVertex2f( left, top);
	glEnd();
}
void fillBox(
		float left, float top,
		float width, float height,
		DWORD color)
{
	if(color)
		setColor(color);
	glBegin(GL_QUADS);
		glVertex2f( left, top);
		glVertex2f( left, top+height);
		glVertex2f( left+width, top+height);
		glVertex2f( left+width, top);
	glEnd();
}
void drawLineIndicator(
		float left,float top,
		float width, float height,
		float ratio, float line, int direction,
		int startColor, int  middleColor, int  endColor,
		int bgColor, int frameColor)
{
	if(!width||!height)
		return;
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glTranslatef(left,top,0);

	glPushAttrib(GL_ENABLE_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);
	
	if(bgColor)
	{
		setColor(bgColor);
		glBegin(GL_QUADS);
			glVertex2f( 0, 0);
			glVertex2f( width, 0);
			glVertex2f( width, height);
			glVertex2f( 0, height);
		glEnd();
	}
	if(startColor)
	{
		if(endColor)
		{
			BYTE *color1,*color2,*color3;
			float result[3];
			color1=INT_TO_BYTE(startColor),
			color2=INT_TO_BYTE(middleColor),
			color3=INT_TO_BYTE(endColor);
			if(middleColor)
			{
				if(ratio<0.5f)
				{
					result[0]=float(color1[0]*(1-2*ratio)+color2[0]*(2*ratio))/255;
					result[1]=float(color1[1]*(1-2*ratio)+color2[1]*(2*ratio))/255;
					result[2]=float(color1[2]*(1-2*ratio)+color2[2]*(2*ratio))/255;
				}
				else
				{
					result[0]=float(color2[0]*(2-2*ratio)+color3[0]*(2*ratio-1))/255;
					result[1]=float(color2[1]*(2-2*ratio)+color3[1]*(2*ratio-1))/255;
					result[2]=float(color2[2]*(2-2*ratio)+color3[2]*(2*ratio-1))/255;
				}
			}
			else
			{
				result[0]=float(color1[0]*(1-ratio)+color3[0]*(ratio))/255;
				result[1]=float(color1[1]*(1-ratio)+color3[1]*(ratio))/255;
				result[2]=float(color1[2]*(1-ratio)+color3[2]*(ratio))/255;
			}
			glColor3fv(result);
		}
		else
		{
			setColor(startColor);
		}
	}
	else
	{
		setColor(INT_RED);
	}
	if(ratio>1)
		ratio=1;
	else if(ratio<0)
		ratio=0;
	glBegin(GL_QUADS);
	switch(direction)
	{
	case DIR_DOWN:
		glVertex2f( 0, 0);						// (0,0)
		glVertex2f( width, 0);					// (1,0)
		glVertex2f( width, ratio*height);		// (1,1)
		glVertex2f( 0, ratio*height);			// (0,1)
		break;
	case DIR_UP:
		glVertex2f( 0, height*(1-ratio));		// (0,0)
		glVertex2f( width, height*(1-ratio));	// (1,0)
		glVertex2f( width, height);				// (1,1)
		glVertex2f( 0, height);					// (0,1)
		break;
	case DIR_LEFT:
		glVertex2f( width*(1-ratio), 0);		// (0,0)
		glVertex2f( width, 0);					// (1,0)
		glVertex2f( width, height);				// (1,1)
		glVertex2f( width*(1-ratio), height);	// (0,1)
		break;
	default:
		glVertex2f( 0, 0);						// (0,0)
		glVertex2f( ratio*width, 0);			// (1,0)
		glVertex2f( ratio*width, height);		// (1,1)
		glVertex2f( 0, height);					// (0,1)
	}
	glEnd();
	if(frameColor&&line)
	{
		glLineWidth(line);
		glBegin(GL_LINE_STRIP);
			setColor(frameColor);
			glVertex2f( 0, 0);
			glVertex2f( width, 0);
			glVertex2f( width, height);
			glVertex2f( 0, height);
			glVertex2f( 0, 0);
		glEnd();
	}
	
	glPopMatrix();
	glPopAttrib();
}
void drawMapIndicator(
		float left,float top,
		float width, float height,
		float ratioX, float ratioY, float line,
		int markColor, int bgColor, int frameColor, int gridColor,
		int gridx, int gridy, float grid)
{
	glPushMatrix();
	glTranslatef(left,top,0);

	glPushAttrib(GL_ENABLE_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);
	
	if(bgColor)
	{
		setColor(bgColor);
		glBegin(GL_QUADS);
			glVertex2f( 0, 0);
			glVertex2f( width, 0);
			glVertex2f( width, height);
			glVertex2f( 0, height);
		glEnd();
	}
	if(gridColor)
	{
		glBegin(GL_LINES);
		setColor(gridColor);
		glLineWidth(grid);
		int i;
		if(gridx>0)
		{
			float dw=width/(gridx+1);
			for(i=1;i<=gridx;i++)
			{
				glVertex2f(i*dw, 0);
				glVertex2f(i*dw, height);
			}
		}
		if(gridy>0)
		{
			float dh=height/(gridy+1);
			for(i=1;i<=gridy;i++)
			{
				glVertex2f(0, i*dh);
				glVertex2f(width, i*dh);
			}
		}
		glEnd();
	}
	glLineWidth(line);
	if(markColor)
	{
		setColor(markColor);
	}
	else
	{
		setColor(INT_RED);
	}
	if(ratioX>1)
		ratioX=1;
	else if(ratioX<0)
		ratioX=0;
	if(ratioY>1)
		ratioY=1;
	else if(ratioY<0)
		ratioY=0;
	glBegin(GL_LINES);
		glVertex2f( ratioX*width, 0);
		glVertex2f( ratioX*width, height);
		glVertex2f( 0, (1-ratioY)*height);
		glVertex2f( width, (1-ratioY)*height);
	glEnd();
	if(frameColor)
	{
		setColor(frameColor);
		glBegin(GL_LINE_STRIP);
			glVertex2f( 0, 0);
			glVertex2f( width, 0);
			glVertex2f( width, height);
			glVertex2f( 0, height);
			glVertex2f( 0, 0);
		glEnd();
	}
	
	glPopMatrix();
	glPopAttrib();
}
void drawCercleIndicator(
		float cx,float cy,
		float x, float y,
		float R, float line,
		int markColor, int frameColor, int bgColor,
		BOOL cercle, int n, BOOL norm)
{
	BOOL error=FALSE;

	glPushMatrix();
	glTranslatef(cx,cy,0);

	glPushAttrib(GL_ENABLE_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);

	if(R<=0)
	{
		R=_hypotf(x,y);
	}
	else if(norm)
	{
		float tmp=_hypotf(x,y);
		if(tmp)
		{
			x*=R/tmp;
			y*=R/tmp;
		}
		else
		{
			error=TRUE;
		}
	}
	glLineWidth(line);
	if(bgColor&&cercle)
	{
		setColor(bgColor);
		fillCercle(0,0,R,n);
	}
	glBegin(GL_LINES);
		if(frameColor)
		{
			setColor(frameColor);
			glVertex2f(-R, 0);
			glVertex2f( R, 0);
			glVertex2f( 0,-R);
			glVertex2f( 0, R);
		}
		markColor?setColor(markColor):setColor(INT_RED);
		if(!error)
		{
			glVertex2f(0,0);
			glVertex2f(x,y);
		}
	glEnd();
	if(frameColor&&cercle)
	{
		setColor(frameColor);
		drawCercle(0,0,R,n);
	}
	
	glPopMatrix();
	glPopAttrib();
}
void drawSphereIndicator(
		float cx,float cy ,float cz,
		float x, float y, float z,
		float R, float line,
		int markColor, int frameColor, int bgColor,
		BOOL cercle, int n, BOOL norm)
{
	BOOL error=FALSE;
	glPushMatrix();
	glTranslatef(cx,cy,cz);

	glPushAttrib(GL_ENABLE_BIT);
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);

	if(R<=0)
	{
		R=sqrt(x*x+y*y+z*z);
	}
	else if(norm)
	{
		float tmp=sqrt(x*x+y*y+z*z);
		if(tmp)
		{
			x*=R/tmp;
			y*=R/tmp;
			z*=R/tmp;
		}
		else
		{
			error=TRUE;
		}
	}
	if(bgColor&&cercle)
	{
		setColor(bgColor);
		fillCercle(0,0,R,n);
	}
	glLineWidth(line);
	glBegin(GL_LINES);
		if(frameColor!=NULL)
		{
			setColor(frameColor);
			glVertex3f(-R, 0, 0);
			glVertex3f( R, 0, 0);
			glVertex3f( 0,-R, 0);
			glVertex3f( 0, R, 0);
			glVertex3f( 0, 0,-R);
			glVertex3f( 0, 0, R);
		}
		if(markColor)
		{
			setColor(markColor);
		}
		else
		{
			setColor(INT_RED);
		}
		if(!error)
		{
			glVertex3f(0,0,0);
			glVertex3f(x,y,z);
		}
	glEnd();
	if(frameColor&&cercle)
	{
		setColor(frameColor);
		drawCercle(0,0,R,n);
	}
	
	glPopMatrix();
	glPopAttrib();
}
void setClearColor(DWORD color)
{
	BYTE *rgb=INT_TO_BYTE(color);
	glClearColor(	BYTE_TO_FLOAT(rgb[RED]),
					BYTE_TO_FLOAT(rgb[GREEN]),
					BYTE_TO_FLOAT(rgb[BLUE]),
					BYTE_TO_FLOAT(rgb[ALPHA]));
}
void createLight(GLenum id, GLfloat *ambient, GLfloat *diffuse, GLfloat *position, GLfloat *specular, GLfloat *attenuation)
{
	glLightfv(id, GL_AMBIENT, ambient);				// Setup The Ambient Light
	glLightfv(id, GL_DIFFUSE, diffuse);				// Setup The Diffuse Light
	glLightfv(id, GL_POSITION, position);			// Position The Light
	if(specular)glLightfv(id, GL_SPECULAR, specular);
	if(attenuation)
	{
		glLightf(id, GL_CONSTANT_ATTENUATION, attenuation[0]);
		glLightf(id, GL_LINEAR_ATTENUATION, attenuation[1]);
		glLightf(id, GL_QUADRATIC_ATTENUATION, attenuation[2]);
	}
	glEnable(id);										// Enable Light One
}
void printMatrix( Font *matrixFont,
				  CMATRIX m, int rows, int cols,
				  CSTR name,
				  float x0, float y0,
				  float dx, float dy,
				  Font *titleFont,
				  CSTR format,
				  int colorPeriod,
				  DWORD color0, DWORD color1, DWORD color2, DWORD color3)
{
	DWORD color;
	int i,j;
	if(!m||!matrixFont)
		return;
	if(!titleFont)
		titleFont=matrixFont;
	if(name)
	{
		titleFont->color=color0;
		titleFont->x=x0;
		titleFont->y=y0;
		glPrint(titleFont,name);
	}
	matrixFont->color=color0;
	matrixFont->x=x0+20;
	for(j=0;j<cols;j++)
	{
		matrixFont->y=y0+20;
		for(i=0;i<rows;i++)
			matrixFont->y+=dy;
		glPrint(matrixFont,_T("%d"),j);
		matrixFont->x+=dx;
	}
	matrixFont->x=x0;
	matrixFont->y=y0+20;
	for(i=0;i<rows;i++)
	{
		glPrint(matrixFont,_T("%d"),i);
		matrixFont->y+=dy;
	}
	matrixFont->x=x0+20;
	if(rows&&cols)
	{
		for(j=0;j<cols;j++)
		{
			matrixFont->y=y0+20;
			if(colorPeriod&&(j/colorPeriod)%2)
				color=color2;
			else
				color=color1;
			for(i=0;i<rows;i++)
			{
				if(colorPeriod&&(i/colorPeriod)%2)
					matrixFont->color=colorMix(color,color3,0.5);
				else
					matrixFont->color=color;
				glPrint(matrixFont,format,m[i][j]);
				matrixFont->y+=dy;
			}
			matrixFont->x+=dx;
		}
	}
}
void setLight(BOOL light)
{
	if (light)
		glEnable(GL_LIGHTING);
	else
		glDisable(GL_LIGHTING);
}
void setSmooth(BOOL smooth, GLenum quality)
{
	if (smooth)
	{
		glEnable(GL_LINE_SMOOTH);
		glEnable(GL_POINT_SMOOTH);
		glEnable(GL_POLYGON_SMOOTH);
		glHint(GL_POINT_SMOOTH_HINT, quality);
		glHint(GL_LINE_SMOOTH_HINT, quality);
		glHint(GL_POLYGON_SMOOTH_HINT, quality);
	}
	else
	{
		glDisable(GL_LINE_SMOOTH);
		glDisable(GL_POINT_SMOOTH);
		glDisable(GL_POLYGON_SMOOTH);
	}
}
void setLineStipple(BOOL stipple, GLushort pattern, GLint repeat)
{
	if(stipple)
	{
		glEnable(GL_LINE_STIPPLE);
		glLineStipple(repeat,pattern);
	}
	else
	{
		glDisable(GL_LINE_STIPPLE);
	}
}
void setDepthTest(BOOL test, GLclampd depth, GLenum func)
{
	if(test)
	{
		glEnable(GL_DEPTH_TEST);
		glClearDepth(depth);
		glDepthFunc(func);	
	}
	else
	{
		glDisable(GL_DEPTH_TEST);
	}
}
void createFog(GLfloat start, GLfloat end, GLfloat *color, GLfloat density, GLuint func, GLenum quality)
{
		glFogi(GL_FOG_MODE, func);
		glFogf(GL_FOG_DENSITY, density);
		glFogf(GL_FOG_START, start);
		glFogf(GL_FOG_END, end); 
		if(color)
			glFogfv(GL_FOG_COLOR, color);
		glHint(GL_FOG_HINT, quality);
}
void setBlend(BOOL blend, GLenum source, GLenum destin)
{
	if (blend)
	{
		glEnable(GL_BLEND);
		glBlendFunc(source, destin);
	}
	else
	{
		glDisable(GL_BLEND);
	}
}
void setPolygonOffset(BOOL enable, GLfloat factor, GLfloat units)
{
	if (enable)
	{
		glEnable(GL_POLYGON_OFFSET_FILL);
		glPolygonOffset(factor,units);
	}
	else
	{
		glDisable(GL_POLYGON_OFFSET_FILL);
	}
}
void setTexturing(BOOL enable)
{
	if (!enable)
	{
		glDisable(GL_TEXTURE_2D);
		glEnable(GL_COLOR_MATERIAL);
	}
	else
	{
		glEnable(GL_TEXTURE_2D);
		glDisable(GL_COLOR_MATERIAL);
	}
}
void fillPolygon(BOOL fill)
{
	if(fill)
	{
		glPolygonMode( GL_BACK, GL_FILL ); // Back Face Is Filled In
		glPolygonMode( GL_FRONT, GL_FILL ); // Front Face Is Drawn With Lines
		glEnable(GL_TEXTURE_2D);
	}
	else
	{
		glPolygonMode( GL_BACK, GL_LINE ); // Back Face Is Filled In
		glPolygonMode( GL_FRONT, GL_LINE ); // Front Face Is Drawn With Lines
		glDisable(GL_TEXTURE_2D);
	}
}

}; // namespace Lio {